#include "Inode.h"
#include "OpenFile.h"
#include "Directory.h"
#include "utility/const.h"
//struct voor data
#include "utility/filsys.h"
#include <list>
#include <map>
#include <algorithm>

using namespace std;

/**
 * Een FileSysteem object is de interne representatie
 * van een gemount filesysteem.
 * Het is tevens de "facade" voor de achterliggende
 * verzameling classes/objecten die de verdere functionaliteit
 * realiseren.
 */

//The offset is set to offset bytes.
#define SEEK_SET 5432001
//The offset is set to its current location plus offset bytes.
#define SEEK_CUR 5432002

class FileSystem {
public:
	/**
	 * Creeer een instantie van een filesysteem dat fysiek
	 * is opgeslagen op het gegeven device (of in een file).
	 * b.v.  new FileSystem( "/dev/floppy" );
	 *   of  new FileSystem( "/hio/praktikum/.../floppie") ;
	 *
	 * Constraints:
	 * - Er mag ten alle tijden maar 1 instantie van een
	 *   filesysteem (kunnen) bestaan voor het gegeven device.
	 *   NB Dat er via links synoniemen van "device" kunnen
	 *      bestaan valt buiten de opdracht.
	 */
	FileSystem(const char * device);

	/** Zoek een inode op, gegeven het absolute pad.
	 *  Als de file niet gevonden wordt zijn er,
	 *  naar keuze, twee opties:
	 * OPTIE 1:
	 *	return waarde wordt NULL
	 * OPTIE 2:
	 *	gebruik het c++ exception mechanisme
	 */
	Inode * findInode(const char * absolute_path);

	/** Zoek een file op in de gegeven directory.
	 *  PAS OP Dit is een 'naam' en GEEN 'pad' !
	 *  Verder analoog aan findInode.
	 *  Vergeet niet te controleren of die inode
	 *  inderdaad een directory is ;-)
	 */
	Inode * findFile(Inode * dirptr, const char * filename);

	/* Voor beide bovenstaande methodes geldt dat
	 * er nooit meer dan 1 instantie van een bepaalde
	 * inode mag bestaan.
	 *
	 * Voorbeeld:
	 *	Inode  * ip1 = findInode( "/" );
	 *	Inode  * ip2 = findFile( ip1, "." );
	 *
	 * dan moet gelden dat ip1 === ip2 !!
	 */

	/** Als de "aanroeper" een bepaalde inode niet meer nodig
	 * heeft moet dat kenbaar gemaakt worden via 'releaseInode'.
	 * Deze methode verlaagt de "gebruiks" teller zodat "het"
	 * systeem kan weten welke inode nu (wel/niet) in gebruik zijn.
	 */
	void releaseInode(Inode * ip);

	/** Geef de verzameling van alle Inodes die nu in gebruik zijn.
	 * NB Het exacte datatype van deze methode moet men zelf bedenken.
	 * >>Doen gewoon list
	 */
	list<Inode*> getAIT();

	// --------------- unix systemcall equivalenten --------------

	// Deze methodes moeten zich zo veel mogelijke gedragen
	// als hun echte tegenhangers.

	OpenFile * open(const char * path);
	int read(OpenFile * fileptr, char buffer[], int count);
	int lseek(OpenFile * fileptr, long offset, int whence);
	void close(OpenFile * fileptr);

	// ------------- "internal" --------------

	/** Geef de verzameling van alle OpenFiles die nu in gebruik zijn.
	 * NB Het exacte datatype van deze methode moet men zelf bedenken.
	 *  >>Doen gewoon list
	 */

	list<OpenFile*> getSOFT();

	void print();

	void browse();

	Directory * main_dir;
	std::map<int, File*> files;

private:
	/*
	 * Filesystem info
	 */
	byte * data;
	unsigned short s_isize; /* size in blocks of i-list */
	daddr_t s_fsize; /* size in blocks of entire volume */
	short s_nfree; /* number of addresses in s_free */
	daddr_t s_free[NICFREE];/* free block list (stack) */
	short s_ninode; /* number of i-nodes in s_inode */
	ino_x s_inode[NICINOD];/* free i-node list (stack) */
	byte s_flock; /* lock during free list manipulation */
	byte s_ilock; /* lock during i-list manipulation */
	byte s_fmod; /* super block modified flag */
	byte s_ronly; /* mounted read-only flag */
	time_t s_time; /* last super block update */
	daddr_t s_tfree; /* total free blocks*/
	ino_x s_tinode; /* total free inodes */
	short s_m; /* interleave factor */
	short s_n; /* " " (cylinder size) */
	char s_fname[6]; /* file system name */
	char s_fpack[6]; /* file system pack name */

	list<Inode*> inodes;
	list<Inode*> AIT;
	list<OpenFile*> SOFT;

};
